Skip to content

Zendesk digest: link unflagged tickets, list new non-English ones; sogs-ban: show posted names - #76

Merged
Bilb merged 14 commits into
mainfrom
feat/digest-translate-every-ticket
Oct 15, 2026
Merged

Bilb merged 14 commits into
mainfrom
feat/digest-translate-every-ticket

Conversation

@Bilb

@Bilb Bilb commented Oct 14, 2026 •

Copy link
Copy Markdown
Collaborator

Three changes in one PR so they can be iterated on together.

The digest links every ticket it reports

A ticket the model did not mark worth_looking_into was only counted in the header's category tally, then recorded as reported, so it never reached Discord as a link.

They now get one line per category under the abuse reports, most common first:

💤 🔑 **2** not flagged — #28891, #28880
💤 ❓ **2** not flagged — #28870, #28866

Each category has its own link budget, so a busy category cannot push a rare ticket off. Within a category the oldest are linked first, so what overflows is the newest, which has the most runs left. A ticket past the budget is counted as +N more and not recorded as reported, so it stays eligible for the next run; so do highlights beyond the display cap.

New non-English tickets are listed for claude: english

The digest used to write an English rendering into the "English transcript (do not edit)" field, for carded tickets only, to serve a Comment button that is gone; nothing reads the field now. That step, ZENDESK_ENGLISH_FIELD_ID and the field code are removed, so the digest is read-only again.

Instead, the first line under the header links every ticket reported for the first time whose classified language is not English, with that language:

🌐 **2** new, not in English — #28891 German, #28880 Russian

An agent answers claude: english on each. A blank or unknown language counts as English; a ticket shown again with 🔄 is not listed. The line records nothing as reported.

claude: english keeps the newest turns whole when a conversation is too long, dropping the oldest, and says how many.

sogs-ban shows each account's posted name

Before Continue? [y/N], and in a dry run, each id is listed with the display name from its newest post in each room's last 256 messages (posted as 'X', posted, with no display name, no recent posts). SOGS stores no profiles, so the name only exists in posted messages. A room that cannot be read, reached or parsed is named under the list; it never stops the ban. Checked live against open.getsession.org.

Tests

785 tests pass, ruff clean. New: TestUnflaggedLine and TestNonEnglishLine in test_triage.py, FitTurns in test_note_reply.py, and tests/sogs/test_names.py.

Bilb added 6 commits October 14, 2026 13:18
sogs-ban now lists every id before the confirmation prompt, and in a dry
run, with the display name from its newest post in each room's last 256
messages: "posted as 'X'", "posted, with no display name" or "no recent
posts". A room that cannot be read is named under the list.

Claude-Session: https://claude.ai/code/session_019cnvrsCXsqLykDyQburD2f
A ticket the model did not mark worth looking into was only counted in the
header's category tally, then recorded as reported, so it never reached
Discord as a link at all. #28891, a lost recovery phrase, went that way.

They now get one line under the abuse reports, grouped by category, most
common first, with the same link budget and a count that covers them all.

Claude-Session: https://claude.ai/code/session_019cnvrsCXsqLykDyQburD2f
The digest translated only the tickets it gave a card, a scope set for the
Comment button those cards no longer carry. 2 of the 35 non-English tickets
in the queue had a transcript.

Every ticket the run classifies is now passed to attach_english. Unchanged
tickets are skipped before classification, so each one is translated once
per change in its conversation.

Claude-Session: https://claude.ai/code/session_019cnvrsCXsqLykDyQburD2f
The digest wrote its English rendering into a ticket field that nothing
reads any more. It now posts the private note `claude: english` writes,
with the same marker, so whichever gets there first stops the other
repeating it until the customer writes again.

translate_conversation and english_note are shared by both. The field
code, its env setting and its tests are gone.

Claude-Session: https://claude.ai/code/session_019cnvrsCXsqLykDyQburD2f
@Bilb Bilb changed the title feat(zendesk): write an English transcript for every non-English ticket Zendesk digest: link unflagged tickets, translate every non-English one; sogs-ban: show posted names Oct 14, 2026
- Post the English notes after the digest and its state, so a slow
  translation cannot hold up or time out the post; skip abuse reports.
- One 💤 line per category, each with its own link budget, and record
  only the tickets actually linked: one shared budget let a busy category
  hide a rare ticket and still mark it reported.
- Guard each ticket's whole note step, so no error on one ticket reaches
  the digest.
- A room that cannot be reached is listed as unread rather than aborting
  sogs-ban; SogsError moves to its own module, ending the ban/names cycle.
- Drop references to the removed field code and document the 💤 lines.

Claude-Session: https://claude.ai/code/session_019cnvrsCXsqLykDyQburD2f
Bilb and others added 3 commits October 15, 2026 12:04
The digest translated only weekday mornings, only tickets in its window,
and every translation stretched a job that has a timeout and an alert.

A second Zendesk trigger, on the customer writing, now calls the relay
with "event": "customer", which runs `note_reply --english-if-needed`:
the `claude: english` note when the conversation needs one, never a
command, never on an app-store review, journal only otherwise, and
covered by RELAY_DRY_RUN like the rest of the relay. An unknown event is
a 400 rather than falling through to the commands.

The digest is read-only again. post_english_notes and is_english go;
recent_names no longer raises on a malformed room.

Claude-Session: https://claude.ai/code/session_019cnvrsCXsqLykDyQburD2f
A long conversation was cut to 8000 characters from its end, dropping the customer's
newest message mid-JSON, and the turns cut off counted as already English: a long
English ticket got a transcript on every message, and a long foreign one a note whose
marker covered a turn it never translated. Whole turns now go from the oldest end, the
newest is clipped only when it alone is too long, and the note says how many were left
out.

The relay runs one note_reply at a time per ticket and waits 60 seconds after a
customer message, restarted by each further one, so a burst of messages costs one
translation and two runs cannot both post it. note_reply also re-reads the ticket for
the marker just before writing.
…translating them

The relay no longer translates when the customer writes: no customer event, no
debounce, no --english-if-needed. The digest instead leads its entries with a 🌐 line
linking every ticket reported for the first time that is not in English, with its
language, for an agent to answer `claude: english` on.

`claude: english` keeps the newest turns whole, drops the oldest when the
conversation is too long and says how many, and checks for the transcript again
just before writing it.
@Bilb Bilb changed the title Zendesk digest: link unflagged tickets, translate every non-English one; sogs-ban: show posted names Zendesk digest: link unflagged tickets, list new non-English ones; sogs-ban: show posted names Oct 15, 2026
Bilb and others added 2 commits October 15, 2026 16:27
…ding a stale draft

Once support has replied on a ticket, `claude: draft` gives Claude the conversation
so far and the customer's newest message separately, and asks it to answer only the
newest one without repeating earlier replies. A draft with a public comment after it
is no longer amended, since it was already sent or overtaken.

`claude: reply` no longer sends a draft the customer has written after. It says so
and waits for `claude: reply [n] anyway`.
Bilb added 2 commits October 16, 2026 09:51
- `anyway` only confirms on the command line itself, since the brief runs to
  the end of the note.
- `claude: reply` also asks for confirmation when the draft was already sent.
- The follow-up prompt treats earlier support replies as context, not as a fact
  source, and covers a follow-up the customer has not prompted.
- A draft looks up the customer's side once rather than twice.
Only a reply this tool sent, or the customer writing, makes the next draft start fresh.
@Bilb
Bilb merged commit 3f9c720 into main Oct 15, 2026
2 of 3 checks passed
@Bilb
Bilb deleted the feat/digest-translate-every-ticket branch October 15, 2026 23:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants